/*
 * Copyright (C) 2020 The LINN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.juqimiao.linn.context;

import com.juqimiao.linn.logging.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/** Todo: 在线程池托管下，出现线程复用并且没有清理相应的 ThreadLocal， 可能出现意想不到的结果。 */

/**
 * 管理应用程序上下文信息。上下文信息类需要继承与{@link ContextObject}。
 *
 * 使用{@link InheritableThreadLocal}保证在父子线程中的数据共享。
 * {@link ConcurrentHashMap}使用黑白树算法，具有更好的性能。
 *
 * @author Collin Cai
 *
 */
public class ContextManager {

    //private static Logger logger = Logger.getLogger(ContextManager.class);

    /* 为每个线程保持一个ContextObject List. InheritableThreadLocal可以提供在父子线程之间的数据共享*/

    static ConcurrentMap<Class<? extends ContextObject>, InheritableThreadLocal<ContextObject>> contexts = new ConcurrentHashMap<>();

    /**
     * 通过Context类型获取Context数据。
     *
     * @param tClass    一个继承于{@link ContextObject}的类型。
     * @param <T>       ContextObject的Class类型
     * @return          一个上下文对象或 null。
     */
    public static <T extends ContextObject> T getContext(Class<?> tClass) {
        if (contexts.get(tClass) == null
                || contexts.get(tClass).get() == null) {

            return null;
        }
        @SuppressWarnings("unchecked")
        T context = (T) contexts.get(tClass).get();

       /* logger.info(String.format("GetContext: [Thread: %s, Context: %s]", Thread.currentThread(), context)); */

        return context;
    }

    /**
     * 设置多个上下文对象。
     *
     * @param contextObjects 上下文对象数组。
     */
    public static void setContext(ContextObject... contextObjects) {
        Arrays.stream(contextObjects).forEach(co -> {
            if (contexts.get(co.getClass()) == null) {
                synchronized (ContextManager.class) {
                    InheritableThreadLocal<ContextObject> threadLocal = new InheritableThreadLocal<>();
                    contexts.putIfAbsent(co.getClass(), threadLocal);
                }
            }
            /* 已经安全获取ThreadLocal可以直接设置Value. */

            contexts.get(co.getClass()).set(co);

//            logger.info(String.format("Set Context: [Thread: %s, Context: %s]",
//                    Thread.currentThread(),
//                    contexts.get(co.getClass())));
        });
    }

    /**
     * 只会移除当前线程中的copy。
     * <p>
     * Todo: 这里有一个challenge, 如何在Web环境中Request执行完毕后安全的remove相应线程的所有数据，或者根本不需要？
     */
    public static void clearAll() {
        contexts.values().forEach(InheritableThreadLocal::remove);
    }
}
